#!/usr/bin/env bash

#########################################################################
# File Name: install_harbor.sh
# Version: V1.0
# Author: Brian Hsiung
# Created Time : 2025-02-25 16:48:23
# Description: 安装Harbor，需要docker 20.10.10-ce+
# docker-compose (v1.18.0+) or docker compose v2 (docker-compose-plugin)
########################################################################

set -ex

# Harbor 版本
declare -r VERSION="v2.12.2"
# Harbor 安装目录
declare -r INSTALL_DIR="/opt"
# Harbor 数据存储目录
declare -r DATA_DIR="/data/harbor/data"
# Harbor 证书存储目录
declare -r CERT_DIR="/data/harbor/cert"
# 阿里云OSS存储地址
declare -r OSS_BASE_URL="https://brianhsiung.oss-cn-hangzhou.aliyuncs.com"
# 当前主机IP
HOST_IP=$(hostname -I|awk '{print $1}')
WORK_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$WORK_DIR" || exit

### 日志函数
log_info()    { echo -e "\033[34m[INFO] $*\033[0m"; }
log_warn()    { echo -e "\033[33m[WARNING] $*\033[0m"; }
log_success() { echo -e "\033[32m[SUCCESS] $*\033[0m"; }
log_error()   { echo -e "\033[31m[ERROR] $*\033[0m"; exit 1; }

### 检查依赖
check_dependency() {
    local -a required_cmds=("wget" "tar" "openssl" "docker")
    local missing_dependencies=()
    
    for cmd in "${required_cmds[@]}"; do
        if ! command -v "$cmd" &>/dev/null; then
            missing_dependencies+=("$cmd")
        fi
    done

    if [ ${#missing_dependencies[@]} -gt 0 ]; then
        log_error "依赖缺失: 请先安装 ${missing_dependencies[*]}"
    fi

    if ! command -v "docker-compose" &>/dev/null && ! docker compose version &>/dev/null; then
        log_error "docker-compose 或 docker compose 未安装"
    fi
}

### 创建自签证书
create_https_cert() {
    log_info "创建HTTPS自签证书"
    
    # 创建证书
    # openssl req -newkey rsa:4096 -nodes -sha256 -keyout "$CERT_DIR/harbor.key" -x509 -days 3650 -subj "/C=CN/ST=Beijing/L=Beijing/O=NiceGroup/OU=Personal/CN=$HOST_IP" -out "$CERT_DIR/harbor.crt"
    # CERT_DIR="/etc/harbor/certs"
    # mkdir -p "$CERT_DIR"
    # openssl req -newkey rsa:4096 -nodes -sha256 \
    #     -keyout "$CERT_DIR/harbor.key" \
    #     -x509 -days 3650 \
    #     -out "$CERT_DIR/harbor.crt" \
    #     -subj "/C=CN/ST=Beijing/L=Beijing/O=NiceGroup/OU=IT/CN=${HOSTNAME}"

    ### Generate a Certificate Authority Certificate
    openssl genrsa -out ca.key 4096

    openssl req -x509 -new -nodes -sha512 -days 3650 \
        -subj "/C=CN/ST=Beijing/L=Beijing/O=NiceGroup/OU=Personal/CN=$HOST_IP" \
        -key ca.key \
        -out ca.crt

    openssl genrsa -out "$HOST_IP".key 4096

    ### Generate a Server Certificate
    openssl req -sha512 -new \
        -subj "/C=CN/ST=Beijing/L=Beijing/O=NiceGroup/OU=Personal/CN=$HOST_IP" \
        -key "$HOST_IP".key \
        -out "$HOST_IP".csr

cat > v3.ext << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
IP=$HOST_IP
EOF

    openssl x509 -req -sha512 -days 3650 \
        -extfile v3.ext \
        -CA ca.crt -CAkey ca.key -CAcreateserial \
        -in "$HOST_IP".csr \
        -out "$HOST_IP".crt

    ### Provide the Certificates to Harbor and Docker

    if [ ! -d "$CERT_DIR" ]; then
        mkdir -p "$CERT_DIR"
    elif [ "$(ls -A $CERT_DIR)" ]; then
        log_error "目录 $DATA_DIR 存在且不为空！"
    fi

    /bin/cp -f "$HOST_IP".crt "$CERT_DIR"
    /bin/cp -f "$HOST_IP".key "$CERT_DIR"

    ### Provide the Certificates to Harbor and Docker
    openssl x509 -inform PEM -in "$HOST_IP".crt -out "$HOST_IP".cert
    [ -d "/etc/docker/certs.d/$HOST_IP" ] || mkdir -p /etc/docker/certs.d/"$HOST_IP"
    /bin/cp -f "$HOST_IP".cert /etc/docker/certs.d/"$HOST_IP"
    /bin/cp -f "$HOST_IP".key /etc/docker/certs.d/"$HOST_IP"
    /bin/cp -f ca.crt /etc/docker/certs.d/"$HOST_IP"
    systemctl restart docker

    log_success "HTTPS自签证书创建完成"

}

install_harbor () {
    
    log_info "安装Harbor"

    # get cpu
    local arch
    local harbor_pkg

    arch=$(uname -m)

    case "$arch" in
        # 这里还未支持arm64架构 
        aarch64|arm64)
            harbor_pkg="harbor-offline-installer-${VERSION}-arm64.tgz" ;;
        amd64|x86_64)
            harbor_pkg="harbor-offline-installer-${VERSION}-amd64.tgz" ;;
        *)
            log_error "不支持的架构: $arch" ;;
    esac

    if [ ! -d "$DATA_DIR" ]; then
        mkdir -p "$DATA_DIR"
    elif [ "$(ls -A $DATA_DIR)" ]; then
        log_error "目录 $DATA_DIR 存在且不为空!"
    fi

    # 下载Harbor
    if [ "$access_mode" = "online" ] && [ ! -f "$harbor_pkg" ]; then
        log_info "下载Harbor: $harbor_pkg"
        wget -q "$OSS_BASE_URL/deploy/harbor/$harbor_pkg" || log_error "下载Harbor失败"
    fi

    [ -f "$harbor_pkg" ] || log_error "本地无 $harbor_pkg，请检查"

    # 解压Harbor
    log_info "解压Harbor: $harbor_pkg"   
    tar -zxf "$harbor_pkg" -C "$INSTALL_DIR"
    cd "$INSTALL_DIR/harbor" || exit

    if [ -f harbor.yml.tmpl ]; then
        mv harbor.yml.tmpl harbor.yml
    elif [ -f harbor.yml ]; then
        /bin/cp -f harbor.yml harbor.yml.bak
    fi

    # sed -i -r -e "s#^hostname:.*#hostname: $HOST_IP#" \
    #     -e "s/port: 80/port: $harbor_port/" \
    #     -e "s#^data_volume:.*#data_volume: $DATA_DIR#" \
    #     -e "s#^[^#|^harbor].*password:.*#  password: "$harbor_db_passwd"#" \
    #     -e 's/^https/#&/g' \
    #     -e 's/^.*port: 443/#&/' \
    #     -e 's/^.*certificate:.*/#&/' \
    #     -e 's/^.*private_key:.*/#&/' harbor.yml

    # sed -i "s;^hostname:.*;hostname: $HOST_IP;" harbor.yml
    # sed -i "s;^.*certificate:.*;  certificate: $CERT_DIR/$HOST_IP.crt;" harbor.yml
    # sed -i "s;^.*private_key:.*;  private_key: $CERT_DIR/$HOST_IP.key;" harbor.yml
    # sed -i 's;password: root123;password: P3pX7typ3nCMPZHDL;' harbor.yml
    # sed -i "s;^data_volume:.*;data_volume: $DATA_DIR;" harbor.yml

    # declare -A test_array=(["test"]=1) &>/dev/null && echo "Supported" || echo "Not supported"
    # 定义替换规则数组（模式匹配 => 替换内容）
    declare -A REPLACE_RULES=(
        # 键值对格式：["sed匹配模式"]="替换内容"
        ["^hostname:"]="hostname: $HOST_IP"
        ["^  certificate:"]="  certificate: $CERT_DIR/$HOST_IP.crt"
        ["^  private_key:"]="  private_key: $CERT_DIR/$HOST_IP.key"
        ["password: root123"]="password: P3pX7typ3nCMPZHDL"
        ["^data_volume:"]="data_volume: $DATA_DIR"
        ["^harbor_admin_password:"]="harbor_admin_password: P3pX73nCMPZHDL"
    )

    # 执行批量替换pattern为key,replacement为value
    for pattern in "${!REPLACE_RULES[@]}"; do
        replacement="${REPLACE_RULES[$pattern]}"
        sed -i "s;${pattern}.*;${replacement};" harbor.yml
    done

    ### 执行安装脚本
    bash ./install.sh

}

### 创建systemd服务
create_harbor_service () {
    log_info "创建Harbor systemd服务"

    if docker compose version >/dev/null 2>&1; then
        cmd="docker compose"
    else
        cmd="docker-compose"
    fi

    cat > /usr/lib/systemd/system/harbor.service << EOF
[Unit]
Description=Harbor Service
Documentation=http://github.com/vmware/harbor
After=docker.service containerd.service
Requires=docker.service

[Service]
Type=simple

ExecStart=/usr/bin/$cmd -f $INSTALL_DIR/harbor/docker-compose.yml up
ExecStop=/usr/bin/$cmd -f $INSTALL_DIR/harbor/docker-compose.yml down

Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

    systemctl daemon-reload
    systemctl enable harbor

    log_success "Harbor systemd服务创建完成"
}

#----------------------------------------------------- Install Harbor -----------------------------------------------------#
main() {

    [ -z "$HOST_IP" ] && log_error "请先配置主机IP"

    [ "$(id -u)" -ne 0 ] && log_error "请使用root用户执行脚本"

    if curl --output /dev/null --silent --head --fail --connect-timeout 5 --max-time 10 "$OSS_BASE_URL"/health; then
        log_info "在线安装Harbor"
        access_mode='online'
    else
        log_info "离线安装Harbor"
        access_mode='offline'
    fi

    if ss -lntu | grep -q ":80 "; then
        log_error "端口 80 已被占用"
    fi

    if ss -lntu | grep -q ":443 "; then
        log_error "端口 443 已被占用"
    fi

    if systemctl is-active --quiet firewalld; then
        log_info "开启防火墙端口"
        firewall-cmd --zone=public --permanent --add-port={80/tcp,443/tcp}
        firewall-cmd --reload
    fi

    # 检查依赖
    check_dependency

    create_https_cert

    install_harbor

    create_harbor_service
}

main